use serde::Serialize;
use thiserror::Error;

pub type IoErr = std::io::Error;
pub type JsonErr = serde_json::Error;


#[derive(Debug, Error)]
pub enum AppErr {

    #[error("io:{0}")]
    Io(#[from] IoErr),

    #[error("json:{0}")]
    Json(#[from] JsonErr),

    #[error("wrap:{0}")]
    Wrap(String),

    #[error("proto:{0}")]
    Proto(&'static str),
}

impl Serialize for AppErr {

    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer {
        serializer.serialize_str(&self.to_string())
    }
}


impl AppErr {

    #[inline]
    pub fn to<T>(self) -> Result<T, AppErr> {
        Err(self)
    }
}
pub trait ToResult<T> {
    
    fn to(self) -> Result<T, AppErr>;
} 

impl <T> ToResult<T> for T {

    #[inline]
    fn to(self) -> Result<T, AppErr> {
        Ok(self)
    }
}

pub trait Wrap<T> {
    
    fn wrap(self) -> Result<T, AppErr>;

    fn wrap_msg(self, msg: &str) -> Result<T, AppErr>;
}

impl <T, E: std::error::Error> Wrap<T> for Result<T, E> {

    fn wrap(self) -> Result<T, AppErr> {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(AppErr::Wrap(e.to_string()))
        }
    }

    fn wrap_msg(self, msg: &str) -> Result<T, AppErr> {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(AppErr::Wrap(format!("{}:{}", msg, e.to_string())))
        }
    }
}

impl <T> Wrap<T> for Option<T> {

    fn wrap(self) -> Result<T, AppErr> {
        match self {
            Some(v) => Ok(v),
            None => Err(AppErr::Wrap("option is none".to_string()))
        }
    }

    fn wrap_msg(self, msg: &str) -> Result<T, AppErr> {
        match self {
            Some(v) => Ok(v),
            None => Err(AppErr::Wrap(msg.to_owned()))
        }
    }
}





